/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.net;

import net.apexes.commons.lang.Checks;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

/**
 * 
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 *
 * @param <R> Response的数据类型
 */
public class JsonHttpClient<R> {

    protected final StringHttpClient client;
    private final JsonEncoder jsonEncoder;
    private final JsonDecoder<R> jsonDecoder;

    public JsonHttpClient(String url, JsonEncoder encoder, JsonDecoder<R> decoder) {
        Checks.verifyNotNull(encoder, "encoder");
        Checks.verifyNotNull(decoder, "decoder");
        this.client = new StringHttpClient(url);
        this.jsonEncoder = encoder;
        this.jsonDecoder = decoder;
    }

    private JsonHttpClient(String url, JsonEncoder encoder) {
        Checks.verifyNotNull(encoder, "encoder");
        this.client = new StringHttpClient(url);
        this.jsonEncoder = encoder;
        this.jsonDecoder = null;
    }

    public String getUrl() {
        return client.getUrl();
    }

    public JsonHttpClient<R>  setCharset(Charset charset) {
        client.setCharset(charset);
        return this;
    }

    public JsonHttpClient<R>  setCharset(String charset) {
        client.setCharset(Charset.forName(charset));
        return this;
    }

    public JsonHttpClient<R> setAcceptCompress(boolean value) {
        client.setAcceptCompress(value);
        return this;
    }

    public JsonHttpClient<R> setSuccessfulResponseCode(int code) {
        client.setSuccessfulResponseCode(code);
        return this;
    }

    public JsonHttpClient<R> setConnectTimeout(int timeout) {
        client.setConnectTimeout(timeout);
        return this;
    }

    public JsonHttpClient<R> setReadTimeout(int timeout) {
        client.setReadTimeout(timeout);
        return this;
    }

    public JsonHttpClient<R> exemptSSL() throws KeyManagementException, NoSuchAlgorithmException {
        client.exemptSSL();
        return this;
    }

    public JsonHttpClient<R> setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
        client.setSSLSocketFactory(sslSocketFactory);
        return this;
    }

    public JsonHttpClient<R> setHostnameVerifier(HostnameVerifier hostnameVerifier) {
        client.setHostnameVerifier(hostnameVerifier);
        return this;
    }

    public JsonHttpClient<R> setRequestHeader(String key, String value) {
        client.setRequestHeader(key, value);
        return this;
    }

    /**
     * @deprecated 使用 {@link #setRequestHeader(String, String)}
     */
    @Deprecated
    public JsonHttpClient<R> setHttpProperty(String key, String value) {
        return setRequestHeader(key, value);
    }

    public JsonHttpClient<R> setSentLogger(StringHttpClient.RequestLogger sentLogger) {
        client.setSentLogger(sentLogger);
        return this;
    }

    public JsonHttpClient<R> setRequestLogger(StringHttpClient.RequestLogger requestLogger) {
        client.setRequestLogger(requestLogger);
        return this;
    }

    public JsonHttpClient<R> setResponseLogger(StringHttpClient.ResponseLogger responseLogger) {
        client.setResponseLogger(responseLogger);
        return this;
    }

    public JsonHttpClient<R> setRequestEncoder(StringHttpClient.RequestEncoder requestEncoder) {
        client.setRequestEncoder(requestEncoder);
        return this;
    }

    public JsonHttpClient<R> setResponseReader(StringHttpClient.ResponseReader responseReader) {
        client.setResponseReader(responseReader);
        return this;
    }

    public R call(Object request) throws Exception {
        return call(request, false);
    }

    public R call(Object request, boolean compress) throws Exception {
        String requestJson = encode(request);
        String responseJson = client.doPost(requestJson, compress);
        return decode(responseJson);
    }

    public R callGet() throws Exception {
        String responseJson = client.doGet();
        return decode(responseJson);
    }

    protected String encode(Object request) throws Exception {
        return jsonEncoder.toJson(request);
    }

    protected R decode(String responseJson) throws Exception {
        if (jsonDecoder == null) {
            return null;
        }
        return jsonDecoder.fromJson(responseJson);
    }

    public static JsonHttpNoticer forNotice(String url, JsonEncoder jsonEncoder) {
        return new JsonHttpNoticer(url, jsonEncoder);
    }

    public static <R> JsonHttpClient<R> forRequest(String url, JsonEncoder jsonEncoder, JsonDecoder<R> jsonDecoder) {
        return new JsonHttpClient<>(url, jsonEncoder, jsonDecoder);
    }

    /**
     *
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     */
    public static class JsonHttpNoticer extends JsonHttpClient<Void> {

        protected JsonHttpNoticer(String url, JsonEncoder jsonEncoder) {
            super(url, jsonEncoder);
        }

        public void notice(Object request) throws Exception {
            notice(request, false);
        }

        public void notice(Object request, boolean compress) throws Exception {
            String requestJson = encode(request);
            client.doPost(requestJson, compress);
        }

    }

    /**
     *
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     */
    public interface JsonEncoder {
        String toJson(Object object) throws Exception;
    }

    /**
     *
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     *
     * @param <R>
     */
    public interface JsonDecoder<R> {
        R fromJson(String json) throws Exception;
    }

}
